<?php

namespace DreamCat\FrameCore\Cases\Controller;

use Components\Utils\Funcs\ArrayFunc;
use DreamCat\FrameCore\Factory\Impl\Container\DefaultContainerFactory;
use DreamCat\FrameCore\Factory\Impl\Controller\DefaultControllerFactory;
use DreamCat\FrameCore\HelperClass\Controller\DemoController;
use DreamCat\FrameCore\HelperClass\Filter\DemoFilter;
use DreamCat\FrameCore\HelperClass\Interceptor\DemoInterceptor;
use DreamCat\FrameCore\HelperClass\Protocol\DemoProtocol;
use DreamCat\FrameCore\ServerMessage\Response\JsonExResponse;
use DreamCat\FrameInterface\ConfigReader;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UriInterface;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Diactoros\Response\TextResponse;

/**
 * 测试控制器工厂
 * @author vijay
 */
class ControllerFactoryTest extends TestCase
{
    /**
     * 测试过滤器
     * @return void
     */
    public function testFilter()
    {
        $expectBody = [
            "s" => uniqid(),
            "time" => microtime(),
        ];
        $container = $this->createContainer(["filters" => [DemoFilter::class]]);
        $request = $this->getMockForAbstractClass(ServerRequestInterface::class);
        $request->expects(self::once())
            ->method("getParsedBody")
            ->willReturn($expectBody);

        /** @var DefaultControllerFactory $factory */
        $factory = $container->get(DefaultControllerFactory::class);
        $response = $factory->httpHandle($container, $request);
        self::assertEquals(TextResponse::class, get_class($response), "回复的类型不一致");
        self::assertEquals(serialize($expectBody), strval($response->getBody()), "回复的消息体不一致");
        self::assertEquals(305, $response->getStatusCode(), "回复的消息码不一致");
    }

    /**
     * 生成指定配置的容器
     * @param array $config 配置
     * @return ContainerInterface 容器
     */
    private function createContainer(array $config): ContainerInterface
    {
        $configReader = $this->getMockForAbstractClass(ConfigReader::class);
        $configReader->method("get")
            ->willReturnCallback(
                function (string $path, $defaultValue = null) use ($config) {
                    return ArrayFunc::getArrayChild($config, $path, $defaultValue);
                }
            );
        return (new DefaultContainerFactory())->create($configReader);
    }

    /**
     * 测试拦截器
     * @return void
     */
    public function testInterceptor()
    {
        $expectBody = [
            "s" => uniqid(),
            "time" => microtime(),
        ];
        $ctl = DemoController::class;
        $act = "index";
        $container = $this->createContainer(
            [
                "interceptors" => [DemoInterceptor::class],
                "router" => ["uris" => ["/test" => "{$ctl}::{$act}"]],
            ]
        );

        $uri = $this->getMockForAbstractClass(UriInterface::class);
        $uri->expects(self::once())
            ->method("getPath")
            ->willReturn("/test");
        $request = $this->getMockForAbstractClass(ServerRequestInterface::class);
        $request->expects(self::once())
            ->method("getParsedBody")
            ->willReturn($expectBody);
        $request->expects(self::once())
            ->method("getMethod")
            ->willReturn("GET");
        $request->expects(self::once())
            ->method("getUri")
            ->willReturn($uri);

        /** @var DefaultControllerFactory $factory */
        $factory = $container->get(DefaultControllerFactory::class);
        $response = $factory->httpHandle($container, $request);
        self::assertEquals(JsonResponse::class, get_class($response), "回复的类型不一致");
        self::assertEquals(
            json_encode(
                [
                    "body" => $expectBody,
                    "ctl" => $ctl,
                    "act" => $act,
                ]
            ),
            strval($response->getBody()),
            "回复的消息体不一致"
        );
        self::assertEquals(599, $response->getStatusCode(), "回复的消息码不一致");
    }

    /**
     * 测试协议配置
     * @param array $protocolConfig 协议配置
     * @param string $key uri的关键字
     * @return void
     * @dataProvider protocolData
     */
    public function testProtocol(array $protocolConfig, string $key)
    {
        $url = "/test/{$key}";
        $ctl = DemoController::class;
        $act = "protocol";

        $uri = $this->getMockForAbstractClass(UriInterface::class);
        $uri->expects(self::once())
            ->method("getPath")
            ->willReturn($url);
        $request = $this->getMockForAbstractClass(ServerRequestInterface::class);
        $request->expects(self::once())
            ->method("getMethod")
            ->willReturn("GET");
        $request->expects(self::once())
            ->method("getUri")
            ->willReturn($uri);

        $container = $this->createContainer(
            [
                "protocol" => $protocolConfig,
                "router" => ["uris" => ["/test/{key}" => "{$ctl}::{$act}"]],
            ]
        );

        /** @var DefaultControllerFactory $factory */
        $factory = $container->get(DefaultControllerFactory::class);
        $response = $factory->httpHandle($container, $request);
        self::assertEquals("{\"key\":\"{$key}::suffer\"}", strval($response->getBody()), "回复的消息体不一致");
        self::assertEquals(JsonExResponse::class, get_class($response), "回复的类型不一致");
        self::assertEquals(200, $response->getStatusCode(), "回复的消息码不一致");
    }

    /**
     * 协议测试的测试用例
     * @return array
     */
    public function protocolData()
    {
        return [
            [
                [
                    "default" => DemoProtocol::class,
                ],
                uniqid("key-"),
            ],
            [
                ["ctl" => [DemoController::class => DemoProtocol::class]],
                microtime(),
            ],
            [
                ["act" => [DemoController::class => ["protocol" => DemoProtocol::class]]],
                microtime(),
            ],
        ];
    }
}

# end of file
